package com.njtoyo.taxi.admin.config;

import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.teaopenapi.models.Config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.njtoyo.taxi.admin.config.cache.CacheConfig;
import com.njtoyo.taxi.admin.library.Const;
import com.njtoyo.taxi.admin.library.sms.AliYunSmsConfig;
import com.taxi.entity.json.DateCustomerDeserializer;
import com.taxi.entity.json.DateCustomerSerializer;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

import java.util.Date;

/**
 * @Author Dell
 * @Date 2021/5/31 13:28
 */
@Configuration
@EnableCaching
@EnableJpaAuditing
@EnableJpaRepositories(value = {"com.njtoyo.taxi.admin.repository", "com.njtoyo.taxi.migration.repository"})
@MapperScan("com.njtoyo.taxi.admin.mapper")
@EntityScan(value = {"com.njtoyo.netrides.entity","com.njtoyo.taxi.entity.backend","com.njtoyo.taxi.migration.entity"})
@ServletComponentScan(basePackages = "com.njtoyo.taxi.admin.security.filter")
@EnableAsync
@EnableScheduling
@SuppressWarnings("unchecked")
@ComponentScan(basePackages = "com.njtoyo.taxi.migration")
@RequiredArgsConstructor
public class AppConfig {
    private final CacheConfig cacheConfig;
    private final AliYunSmsConfig aliYunSmsConfig;

    @Bean(value = Const.PRIMARY_CACHE_TEMPLATE)
    @Primary
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());

        template.setConnectionFactory(lettuceConnectionFactory);

        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.disableDefaultTyping();
        jacksonSeial.setObjectMapper(mapper);
        template.setValueSerializer(jacksonSeial);
        return template;
    }

    @Bean(value = Const.SECONDARY_CACHE_TEMPLATE)
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        LettuceConnectionFactory factory = buildSecondaryFactory();
        template.setConnectionFactory(factory);
        return template;
    }

    @Bean(value = Const.PRIMARY_CACHE_MANAGER)
    @Primary
    public CacheManager primaryCacheManager(LettuceConnectionFactory factory) {
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(this.redisCacheConfiguration())
                .build();
        return cacheManager;
    }

    @Bean(value = Const.SECONDARY_CACHE_MANAGER)
    public CacheManager secondaryCacheManager() {
        LettuceConnectionFactory factory = buildSecondaryFactory();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(this.redisCacheConfiguration())
                .build();
        return cacheManager;
    }

    public RedisCacheConfiguration redisCacheConfiguration(){
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .computePrefixWith((prefix) -> prefix + ":")
                .disableCachingNullValues();

        return config;
    }

    @Bean
    public WxCpService wxCpService(){
        WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
        WxCpService wxCpService = new WxCpServiceImpl();
        wxCpService.setWxCpConfigStorage(config);
        return wxCpService;
    }

    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("TPTS");
        return threadPoolTaskScheduler;
    }

    @Bean
    public ClassLoaderTemplateResolver emailTemplateResolver(){
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        resolver.setPrefix("mail/");
        resolver.setTemplateMode("HTML");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setOrder(1);
        return resolver;
    }

    @Bean
    public ObjectMapper objectMapper(){
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(Date.class, new DateCustomerSerializer()).addDeserializer(Date.class,new DateCustomerDeserializer());
        return new ObjectMapper().registerModule(javaTimeModule).setSerializationInclusion(JsonInclude.Include.ALWAYS);
    }

    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer(){
        return configuration -> configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    private LettuceConnectionFactory buildSecondaryFactory(){
        RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
        redisConfiguration.setHostName(cacheConfig.getSecondary().getHostName());
        redisConfiguration.setPassword(cacheConfig.getSecondary().getPassword());
        redisConfiguration.setDatabase(cacheConfig.getSecondary().getDatabase());
        redisConfiguration.setPort(cacheConfig.getSecondary().getPort());
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfiguration);
        factory.afterPropertiesSet();
        return factory;
    }

    @Bean("aliYunSmsClient")
    public Client createClient() throws Exception {
        Config config = new Config();
        config.setAccessKeyId(aliYunSmsConfig.getAccessKeyId())
                .setAccessKeySecret(aliYunSmsConfig.getAccessKeySecret())
                .setEndpoint("dysmsapi.aliyuncs.com");

        return new Client(config);
    }
}
